#include "miio_command.h"
#include "json/jsmi.h"
#include "json/fmt.h"
#include "miio_user_api.h"
#include "mible_log.h"
#include "third_party/mbedtls/base64.h"
#include "third_party/mbedtls/md5.h"
#include "common/crc32.h"

#undef MIIO_LOG_TAG
#define MIIO_LOG_TAG			"mcmd_uart"

#if MI_LOCK_UART_ENABLE

/*
* UART communication protocol
* +---------------------------------+--------+---------+---------------------+
* | DATA ELEMENT                    | FIELD  | LENGTH  | REMARKS             |
* +---------------------------------+--------+---------+---------------------+
* | Header                          | Value  | 2 bytes | default:"LU         |
* +---------------------------------+--------+---------+---------------------+
* | Version                         | Value  | 1 byte  | default:0x01        |
* +---------------------------------+--------+---------+---------------------+
* | Source Address                  | Value  | 1 byte  | -                   |
* +---------------------------------+--------+---------+---------------------+
* | Destination  Address            | Value  | 1 byte  | -                   |
* +---------------------------------+--------+---------+---------------------+
* | Payload Length                  | Value  | 2 bytes | -                   |
* +---------------------------------+--------+---------+---------------------+
* | Cmd ID                          | Value  | 2 bytes | -                   |
* +---------------------------------+--------+---------+---------------------+
* | Cmd Length                      | Value  | 2 bytes | -                   |
* +---------------------------------+--------+---------+---------------------+
* | Cmd Value                       | Value  | N bytes | -                   |
* +---------------------------------+--------+---------+---------------------+
* | CRC16                           | Value  | 2 bytes | -                   |
* +---------------------------------+--------+---------+---------------------+
*
*/

typedef enum{
	MCMD_WIFI_IDLE = 0,
	MCMD_WIFI_CONNECTING,
	MCMD_WIFI_CONNECT_AP,
	MCMD_WIFI_CONNECT_CLOUD,
    MCMD_WIFI_ERROR,
    MCMD_WIFI_AUTH_FAILED,
    MCMD_WIFI_URL_OK,
}MCMD_WIFI_STATE;

//static int uart_com = 1;

struct uart_cmd_t{
    int cmd_combine;
    int(*cb_oncmd)(mcmd_uart_t* mcmd_uart, char *params, int length);
};

mcmd_uart_t* p_uart = NULL;
mcmd_uart_t* mcmd_uart_host = NULL;

#ifdef USE_CRC16
/* CRC16 implementation acording to CCITT standards */
static const unsigned short crc16tab[256]= {
	0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
	0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
	0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
	0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
	0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
	0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
	0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
	0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
	0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
	0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
	0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
	0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
	0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
	0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
	0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
	0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
	0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
	0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
	0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
	0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
	0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
	0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
	0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
	0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
	0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
	0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
	0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
	0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
	0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
	0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
	0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
	0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};
  
static unsigned short crc16_ccitt(const unsigned char *buf, int len)
{
	register int counter;
	register unsigned short crc = 0;
	for( counter = 0; counter < len; counter++)
		crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *(char *)buf++)&0x00FF];
	return crc;
}
#else       //USE_CRC16
static unsigned short crc16_ccitt( const unsigned char *buf, int len )
{
	unsigned short crc = 0;
	while( len-- ) {
		int i;
		crc ^= *buf++ << 8;
		
		for( i = 0; i < 8; ++i ) {
			if( crc & 0x8000 )
				crc = (crc << 1) ^ 0x1021;
			else
				crc = crc << 1;
		}
	}
	return crc;
}
#endif  //USE_CRC16

static bool cmd_check_crc(const unsigned char *buf, int len, uint16_t crc16)
{
    uint16_t crc_calc = crc16_ccitt(buf, len);
    
    return (crc_calc==crc16)? true:false;
}

//req:{}
//rsp:{"did:"1050000270", "mac": "E4:AA:EC:46:24:74", "pdid": 8873, "model": "miot.lock.specwb", "bt_version":"2.6.0_0001"}
int cb_oncmd_get_devinfo(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_get_devinfo: %s\n", tmp_buff);
    {
        uint8_t buffer[128] = {0};
        jsmi_composer_t composer = jsmi_composer_create((char *)buffer, sizeof(buffer));
        if(composer != NULL){
            jsmi_set_object_begin(composer);
            //"did:"1050000270"
            uint64_t did = 0;
            uint8_t byte_did[32];
            get_mi_device_id(byte_did);
            did = (byte_did[4]<<24) + (byte_did[5]<<16) + (byte_did[6]<<8) + byte_did[7];
            memset(byte_did, 0, sizeof(byte_did));
            util_u64toa(did, (char *)byte_did);
            jsmi_set_key_value_str(composer, "did", (const char *)byte_did, 1);
            //"mac": "E4:AA:EC:46:24:74"
            uint8_t mac[6] = {0};
            mible_gap_address_get(mac);
            memset(byte_did, 0, sizeof(byte_did));
            sprintf((char *)byte_did,"%02X:%02X:%02X:%02X:%02X:%02X", 
                mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
            jsmi_set_key_value_str(composer, "mac", (const char *)byte_did, 1);
            //"pdid": 8873
            jsmi_set_key_value_uint16(composer, "pdid", PRODUCT_ID, 1);
            //"model": "miot.lock.specwb"
            memset(byte_did, 0, sizeof(byte_did));
            sprintf((char *)byte_did, MODEL_NAME);
            jsmi_set_key_value_str(composer, "model", (const char *)byte_did, 1);
            //"bt_version":"2.6.0_0001"
            memset(byte_did, 0, sizeof(byte_did));
            sprintf((char *)byte_did, MIBLE_LIB_AND_DEVELOPER_VERSION);
            jsmi_set_key_value_str(composer, "bt_version", (const char *)byte_did, 0);
            jsmi_set_object_end(composer, 0);
            MI_LOG_INFO("get_devinfo len %d: %s", composer->js_len, buffer);
            //TODO: send uart TX
            mcmd_uart_send(mcmd_uart, UART_CMD_NETBIND_DEVINFO, (char *)buffer, composer->js_len);
            jsmi_composer_delete(composer);
        }
    }
    mcmd_uart->run_state |= 0x01;

    return MI_SUCCESS;
}

int cb_oncmd_get_netinfo(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_get_netinfo: %s\n", tmp_buff);
    {
        unsigned char state;
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            if(0 == jsmi_get_value_uint8(jsmi_parser, NULL, "wifi_state", &state)){
                extern bool wifi_cfg_rsp;
                wifi_cfg_rsp = true;
                report_wifi_status(state);
                MI_LOG_DEBUG("report_wifi_status: %d\n", state);
            }else{
                MI_LOG_ERROR("not found!\n");
                ret = MI_ERR_NOT_FOUND;
            }
			jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    mcmd_uart->run_state |= 0x01;

    return ret;
}

int cb_oncmd_get_devcert(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_get_devcert: %s\n", tmp_buff);
    {
        mi_scheduler_start(SYS_MSC_CERT_DEV);
    }

    mcmd_uart->run_state  = mcmd_uart->run_state << 1;
    mcmd_uart->run_state |= 0x01;

    return MI_SUCCESS;
}

int cb_oncmd_get_manucert(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_get_manucert: %s\n", tmp_buff);
    {
        mi_scheduler_start(SYS_MSC_CERT_MANU);
    }

    mcmd_uart->run_state  = mcmd_uart->run_state << 1;
    mcmd_uart->run_state |= 0x01;

    return MI_SUCCESS;
}

int cb_oncmd_get_sign(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params!\n");
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_get_sign: %s\n", tmp_buff);
    {
        unsigned char hash[128] = {0};
        unsigned int hash_len = 0;
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            if(0 == jsmi_get_value_str(jsmi_parser, NULL, "hash", (char *)hash, sizeof(hash))){
                ret = mbedtls_base64_decode(hash, sizeof(hash), (size_t *)&hash_len, (const unsigned char *)hash, strlen((const char *)hash));
                MI_LOG_DEBUG("mbedtls_base64_decode err %d, len %d!\n", ret, hash_len);
                MI_LOG_HEXDUMP(hash, hash_len);
                if(0 == ret && hash_len == 32){
                    extern uint8_t lock_hash[32];
                    memcpy(lock_hash, hash, sizeof(lock_hash));
                    mi_scheduler_start(SYS_MSC_SIGN);
                    MI_LOG_DEBUG("mi_scheduler_start: %d\n", SYS_MSC_SIGN);
                }
            }else{
                MI_LOG_ERROR("not found!\n");
                ret = MI_ERR_NOT_FOUND;
            }
			jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    if(ret != 0){
        mcmd_uart_send(mcmd_uart, UART_CMD_NETBIND_SIGN, "{\"result\":\"error\"}", strlen("{\"result\":\"error\"}"));
    }
    return ret;
}

int cb_oncmd_set_netstatus(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_set_netstatus: %s\n", tmp_buff);
    {
        unsigned char state = 0;
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            if(0 == jsmi_get_value_uint8(jsmi_parser, NULL, "wifi_state", &state)){
                extern bool wifi_cfg_rsp;
                wifi_cfg_rsp = true;
                report_wifi_status(state);
                MI_LOG_DEBUG("report_wifi_status: %d\n", state);
            }else{
                MI_LOG_ERROR("not found!\n");
                ret = MI_ERR_NOT_FOUND;
            }
			jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    if(ret == 0){
        mcmd_uart_send(mcmd_uart, UART_CMD_NETBIND_NETSTATUS, "{\"result\":\"ok\"}", strlen("{\"result\":\"ok\"}"));
    }else{
        mcmd_uart_send(mcmd_uart, UART_CMD_NETBIND_NETSTATUS, "{\"result\":\"error\"}", strlen("{\"result\":\"error\"}"));
    }
    return ret;
}

/* uart ota demo */
#define UART_DFU_RETRY_MAX          10//3
#define UART_DFU_RETRY_TIMEOUT      1000
#define UART_DFU_FRAG_SIZE          512

typedef enum {
    MULTIMCU_OTA_ACK_NONE = 0,
    MULTIMCU_OTA_ACK_READY,
    MULTIMCU_OTA_ACK_BUSY,
    MULTIMCU_OTA_ACK_START,
    MULTIMCU_OTA_ACK_END,
    MULTIMCU_OTA_ACK_IGNORE,
    MULTIMCU_OTA_ACK_REFUSE,
    MULTIMCU_OTA_ACK_ERR_TIMEOUT,
    MULTIMCU_OTA_ACK_ERR_LENGTH,
    MULTIMCU_OTA_ACK_ERR_FLASH,
    MULTIMCU_OTA_ACK_ERR_VERIFY,
    MULTIMCU_OTA_ACK_ERR_STATE,
    MULTIMCU_OTA_ACK_MAX
} multimcu_ota_ack_t;

uint32_t dfu_total_legth = 0;
uint32_t dfu_recv_legth = 0;
uint32_t dfu_last_index = 0;
uint32_t dfu_retry_cnt = 0;
uint32_t dfu_crc32 = 0;
uint8_t dfu_name[32] = {0};
uint8_t dfu_version[32] = {0};
uint8_t dfu_md5[33] = {0};
bool dfu_force = false;
bool dfu_in_process = false;
static uint32_t dfu_flash_start, dfu_flash_size;
static void *m_flowctrl_timer = NULL;

void uart_dfu_flush(void)
{
    dfu_total_legth = 0;
    dfu_recv_legth = 0;
    dfu_last_index = 0;
    dfu_retry_cnt = 0;
    dfu_crc32 = 0;
    memset(dfu_name, 0, sizeof(dfu_name));
    memset(dfu_version, 0, sizeof(dfu_version));
    memset(dfu_md5, 0, sizeof(dfu_md5));
    dfu_in_process = false;
    if(m_flowctrl_timer != NULL)
        miio_timer_stop(m_flowctrl_timer);
}

static void flow_ctrl_timeout_handler(void * p_context)
{
    MI_LOG_WARNING("flow_ctrl_timeout.\n");
    mcmd_uart_t* mcmd_uart = (mcmd_uart_t*) p_context;
    
    if(dfu_retry_cnt < UART_DFU_RETRY_MAX){
        dfu_retry_cnt ++;
        mcmd_uart_send(mcmd_uart, UART_CMD_OTA_DATA, "{\"multimcu_ota_data\": \"error\"}", sizeof("{\"multimcu_ota_data\": \"error\"}"));
        miio_timer_start(m_flowctrl_timer, UART_DFU_RETRY_TIMEOUT, (void *)mcmd_uart);
    }else{
        uint8_t buf[128] = {0};
        snprintf((char *)buf, sizeof(buf), 
            "{\"method\":\"multimcu_ota.status\",\"params\":{\"name\":\"%s\",\"address\":%d,\"status\":%d,\"progress\":%d}}",
            dfu_name, UART_CMD_DA_BLE, MULTIMCU_OTA_ACK_ERR_TIMEOUT, 0);
        mcmd_uart_send(mcmd_uart, UART_CMD_OTA_STATUS, (char *)buf, strlen((const char *)buf));
        uart_dfu_flush();
    }
}

int cb_oncmd_ota_info(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params!\n");
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_ota_info: %s, v_length= %d\n", (const char *)tmp_buff, v_length);
//    if(dfu_in_process){
//        ret = MI_ERR_BUSY;
//    }else
    { 
        uart_dfu_flush();
        
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            if(0 == jsmi_get_value_str(jsmi_parser, NULL, "name", (char *)dfu_name, sizeof(dfu_name))){
                MI_LOG_DEBUG("ota_info name: %s\n", dfu_name);
            }else{
                ret = MI_ERR_NOT_FOUND;
            }
            
            if(0 == jsmi_get_value_uint32(jsmi_parser, NULL, "size", &dfu_total_legth)){
                MI_LOG_DEBUG("ota_info size: %d\n", dfu_total_legth);
            }else{
                ret = MI_ERR_NOT_FOUND;
            }
            
            if(0 == jsmi_get_value_str(jsmi_parser, NULL, "version", (char *)dfu_version, sizeof(dfu_version))){
                MI_LOG_DEBUG("ota_info version: %s\n", dfu_version);
            }else{
                ret = MI_ERR_NOT_FOUND;
            }
            
            if(0 == jsmi_get_value_str(jsmi_parser, NULL, "MD5", (char *)dfu_md5, sizeof(dfu_md5))){
                MI_LOG_DEBUG("ota_info MD5: %s\n", dfu_md5);
            }else{
                ret = MI_ERR_NOT_FOUND;
            }
            
            if(0 == jsmi_get_value_bool(jsmi_parser, NULL, "force_ota", &dfu_force)){
                MI_LOG_DEBUG("ota_info force: %d\n", dfu_force);
            }else{
                ret = MI_ERR_NOT_FOUND;
            }
            
			jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    
    // erase flash & set timeout
    if(ret == 0){
        dfu_flash_start = m_config.dfu_start;
        dfu_flash_size = m_config.dfu_size;
        mible_nvm_init();
        //mible_nvm_erase(dfu_flash_size, dfu_flash_start);
        
        if(m_flowctrl_timer == NULL){
            miio_timer_create(&m_flowctrl_timer, flow_ctrl_timeout_handler, MIBLE_TIMER_SINGLE_SHOT);
            if(m_flowctrl_timer == NULL)
                ret = MI_ERR_NO_MEM;
        }
    }
    
    uint8_t status;
    if(ret==0){
        status = MULTIMCU_OTA_ACK_READY;
        dfu_in_process = true;
    }
    else if(ret == MI_ERR_BUSY)
        status = MULTIMCU_OTA_ACK_BUSY;
    else
        status = MULTIMCU_OTA_ACK_REFUSE;
    
    uint8_t buf[128] = {0};
    snprintf((char *)buf, sizeof(buf), 
        "{\"method\":\"multimcu_ota.status\",\"params\":{\"name\":\"%s\",\"address\":%d,\"status\":%d,\"progress\":%d}}",
        dfu_name, UART_CMD_DA_BLE, status, 0);
    mcmd_uart_send(mcmd_uart, UART_CMD_OTA_INFO, (char *)buf, strlen((const char *)buf));

    return ret;
}

int cb_oncmd_ota_data(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MIBLE_ERR_UNKNOWN;
    uint8_t buf[128] = {0};
    uint8_t status = 0, progress = 0;
    
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;
    unsigned int v_index = 0;
    unsigned char* p_index = (unsigned char *)&v_index;
    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params!\n");
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(p_index, params + 11, 4);
    memcpy(tmp_buff, params + 15, v_length - 4);
    MI_LOG_DEBUG("cb_oncmd_ota_data: index=%d, recv_length=%d, data_length= %d\n", v_index, length, v_length - 4);

    uint16_t cmd_crc = params[v_length+11] + ((uint16_t)params[v_length+12] << 8);
    if(cmd_check_crc((const uint8_t *)params, v_length+11, cmd_crc) == false){
        MI_LOG_DEBUG("crc invalid %04x!!\n", cmd_crc);
        mcmd_uart_send(mcmd_uart, UART_CMD_OTA_DATA, "{\"multimcu_ota_data\": \"error\"}", sizeof("{\"multimcu_ota_data\": \"error\"}"));
        miio_timer_start(m_flowctrl_timer, UART_DFU_RETRY_TIMEOUT, (void *)mcmd_uart);
        return MI_ERR_INVALID_PARAM;
    }
    
    if(dfu_in_process == 0){
        ret = MI_ERR_INVALID_STATE;
    }else{
        //write to flash
        if(v_index - dfu_last_index == 1){
            /* Try to write the fragment into flash */
            if(dfu_last_index*UART_DFU_FRAG_SIZE + (v_length - 4) > dfu_flash_size){
                ret = MI_ERR_INVALID_LENGTH;
                goto end;
            }
            mible_nvm_write(tmp_buff, (v_length - 4), dfu_last_index*UART_DFU_FRAG_SIZE + dfu_flash_start);
#if 0
            uint32_t crc32      = v_index == 1 ? 0 : dfu_crc32;
            uint32_t crc32_orig = soft_crc32(tmp_buff, (v_length - 4), crc32);
            
            /* Read the copy of fragment to RAM */
            mible_nvm_read(tmp_buff, (v_length - 4), dfu_last_index*UART_DFU_FRAG_SIZE + dfu_flash_start);
            crc32 = soft_crc32(tmp_buff, (v_length - 4), crc32);
            
            MI_LOG_DEBUG("crc32: %08x, crc32_orig: %08x\n", crc32, crc32_orig);
            /* Check the integrity of fragment */
            if (crc32 == crc32_orig) {
                dfu_crc32 = crc32;
#else
            {
#endif
                dfu_recv_legth += (v_length - 4);
                dfu_last_index++;
                dfu_retry_cnt = 0;
                miio_timer_start(m_flowctrl_timer, UART_DFU_RETRY_TIMEOUT, (void *)mcmd_uart);
                mcmd_uart_send(mcmd_uart, UART_CMD_OTA_DATA, "{\"multimcu_ota_data\": \"ok\"}", sizeof("{\"multimcu_ota_data\": \"ok\"}"));
                /* recv all firmware, TODO: verify MD5*/
                if(dfu_recv_legth >= dfu_total_legth){
                    mbedtls_md5_context ctx;
                    mbedtls_md5_init( &ctx );
                    mbedtls_md5_starts( &ctx );
                    for(int i=0; i<dfu_last_index; i++){
                        uint32_t raw_length = dfu_total_legth - i*UART_DFU_FRAG_SIZE;
                        uint32_t read_length = raw_length<UART_DFU_FRAG_SIZE? raw_length:UART_DFU_FRAG_SIZE;
                        mible_nvm_read(tmp_buff, read_length, i*UART_DFU_FRAG_SIZE + dfu_flash_start);
                        mbedtls_md5_update(&ctx, tmp_buff, read_length);
                    }
                    uint8_t decrypt[16] = {0}, md5out[33] = {0};
                    mbedtls_md5_finish(&ctx, decrypt);
                    mbedtls_md5_free( &ctx );
                    for(int i=0; i<16; i++)
                        sprintf((char *)(md5out+2*i), "%02x", decrypt[i]);
                    
                    MI_LOG_DEBUG("dfu_recv_legth: %d, MD5(info): %s, MD5(flash): %s\n", dfu_recv_legth, dfu_md5, md5out);
                    if(strncmp((const char *)md5out, (const char *)dfu_md5, 32) == 0){
                        ret = MI_SUCCESS;
                    }else{
                        ret = MI_ERR_RESOURCES;
                    }
                }
            }
#if 0
            else{
                //TODO: Flash crc err, Retry
                ret = MI_ERR_INTERNAL;
            }
#endif
        }
        else if(v_index - dfu_last_index == 0){
            miio_timer_start(m_flowctrl_timer, UART_DFU_RETRY_TIMEOUT, (void *)mcmd_uart);
            mcmd_uart_send(mcmd_uart, UART_CMD_OTA_DATA, "{\"multimcu_ota_data\": \"ok\"}", sizeof("{\"multimcu_ota_data\": \"ok\"}"));
        }
    }
end:
    if(ret == MIBLE_ERR_UNKNOWN)
        return MI_SUCCESS;
    else if(ret == MI_ERR_INVALID_STATE)
        status = MULTIMCU_OTA_ACK_ERR_STATE;
    else if(ret == MI_ERR_INVALID_LENGTH)
        status = MULTIMCU_OTA_ACK_ERR_LENGTH;
    else if(ret == MI_ERR_RESOURCES)
        status = MULTIMCU_OTA_ACK_ERR_VERIFY;
    else if(ret == MI_ERR_INTERNAL)
        status = MULTIMCU_OTA_ACK_ERR_FLASH;
    else{
        status = MULTIMCU_OTA_ACK_END;
        progress = 100;
    }
        
    snprintf((char *)buf, sizeof(buf), 
        "{\"method\":\"multimcu_ota.status\",\"params\":{\"name\":\"%s\",\"address\":%d,\"status\":%d,\"progress\":%d}}",
        dfu_name, UART_CMD_DA_BLE, status, progress);
    mcmd_uart_send(mcmd_uart, UART_CMD_OTA_STATUS, (char *)buf, strlen((const char *)buf));
    
    uart_dfu_flush();
    
    return ret;
}

int cb_oncmd_ota_reboot(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_ota_reboot: %s, v_length= %d", tmp_buff, v_length);
    
    mible_upgrade_firmware();

    return MI_SUCCESS;
}

static int hex2byte(char *dst, char *src)
{
    while(*src){
        unsigned int tmp;
        sscanf(src, "%02X", &tmp);
        *dst = tmp;
        src += 2;
        dst++;
    }
    return 0;
}

unsigned char app_pubkey[64] = {0};
unsigned char enc_data[8] = {0};
unsigned char extra_data[10] = {0};

int cb_oncmd_login_req(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params!\n");
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_login_req: %s\n", tmp_buff);
    {
        unsigned char device_id[8] = {0};
        const jsmitok_t *tok_parent = NULL;
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            tok_parent = jsmi_key_value(jsmi_parser, tok_parent, "cmd_params");
            if(NULL != tok_parent){
                //parse pubkey
                char hex_str[129] = {0};
                if(0 == jsmi_get_value_str(jsmi_parser, tok_parent, "pubkey", (char *)hex_str, sizeof(hex_str))){
                    hex2byte((char *)app_pubkey, hex_str);
                    mi_scheduler_start(P2P_LOGIN_START);
                    // mi_schd_set_pubkey(&app_pubkey);
                }else{
                    MI_LOG_ERROR("Cannot find pubkey\n");
                    ret = MI_ERR_NOT_FOUND;
                }
                memset(hex_str, 0 ,sizeof(hex_str));
                if(0 == jsmi_get_value_str(jsmi_parser, tok_parent, "device_id", (char *)hex_str, sizeof(hex_str))){
                    hex2byte((char *)device_id, hex_str);
                    MI_LOG_ERROR("Check did %08x\n", *(uint32_t *)device_id);
                }
            }else{
                MI_LOG_ERROR("Cannot find cmd_params\n");
                ret = MI_ERR_NOT_FOUND;
            }
            jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    if(ret != 0){
        mcmd_uart_send(mcmd_uart, UART_CMD_LOGIN_REQ, "{\"result\":\"error\"}", strlen("{\"result\":\"error\"}"));
    }
    return ret;
}

int cb_oncmd_login_cfm(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int ret = MI_SUCCESS;
    unsigned char tmp_buff[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;

    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR("invalid params!\n");
        return MI_ERR_INVALID_PARAM;
    }
    p_length = (unsigned char *)&v_length;
    p_length[0] = params[9];
    p_length[1] = params[10];

    memcpy(tmp_buff, params + 11, v_length);
    MI_LOG_DEBUG("cb_oncmd_login_cfm: %s\n", tmp_buff);
    {
        const jsmitok_t *tok_parent = NULL;
        jsmi_parser_t jsmi_parser = jsmi_parser_create((const char *)tmp_buff, v_length);
        if(NULL != jsmi_parser){
            tok_parent = jsmi_key_value(jsmi_parser, tok_parent, "cmd_params");
            if(NULL != tok_parent){
                //parse pubkey
                char hex_str[129] = {0};
                if(0 == jsmi_get_value_str(jsmi_parser, tok_parent, "encdata", (char *)hex_str, sizeof(hex_str))){
                    hex2byte((char *)enc_data, hex_str);
                    
                }else{
                    MI_LOG_ERROR("Cannot find encdata\n");
                    ret = MI_ERR_NOT_FOUND;
                }
                
                memset(hex_str, 0 ,sizeof(hex_str));
                unsigned char *p_extra = NULL;
                unsigned char extra_len = 0;
                if(0 == jsmi_get_value_str(jsmi_parser, tok_parent, "extra", (char *)hex_str, sizeof(hex_str))){
                    hex2byte((char *)extra_data, hex_str);
                    p_extra = extra_data;
                    extra_len = 10;
                }
                
                mi_schd_set_authvalue(enc_data, 8, p_extra, extra_len);
            }else{
                MI_LOG_ERROR("Cannot find cmd_params\n");
                ret = MI_ERR_NOT_FOUND;
            }
            jsmi_parser_delete(jsmi_parser);
        }else{
			ret = MI_ERR_NO_MEM;
		}
    }
    if(ret != 0){
        mcmd_uart_send(mcmd_uart, UART_CMD_LOGIN_CFM, "{\"result\":\"error\"}", strlen("{\"result\":\"error\"}"));
    }
    return ret;
}

struct uart_cmd_t g_uart_cmd[] = {
    {UART_CMD_NETBIND_DEVINFO,       cb_oncmd_get_devinfo},
    {UART_CMD_NETBIND_NETINFO,       cb_oncmd_get_netinfo},
    {UART_CMD_NETBIND_DEVCERT,       cb_oncmd_get_devcert},
    {UART_CMD_NETBIND_MANUCERT,      cb_oncmd_get_manucert},
    {UART_CMD_NETBIND_SIGN,          cb_oncmd_get_sign},
    {UART_CMD_NETBIND_NETSTATUS,     cb_oncmd_set_netstatus},
    {UART_CMD_OTA_INFO,              cb_oncmd_ota_info},
    {UART_CMD_OTA_DATA,              cb_oncmd_ota_data},
    {UART_CMD_OTA_REBOOT,            cb_oncmd_ota_reboot},
//REMOTE UNLOCK
    {UART_CMD_LOGIN_REQ,             cb_oncmd_login_req},
    {UART_CMD_LOGIN_CFM,             cb_oncmd_login_cfm},
//SPEC RECV
#if 1
    {UART_CMD_SPEC_GET_RSP,          cb_oncmd_spec_get_rsp},
    {UART_CMD_SPEC_SET_RSP,          cb_oncmd_spec_set_rsp},
    {UART_CMD_SPEC_ACTION_OUT,       cb_oncmd_spec_action_out},
    {UART_CMD_SPEC_PROPS_CHANGED,    cb_oncmd_spec_props_changed},
    {UART_CMD_SPEC_EVENT_OCCURED,    cb_oncmd_spec_event_occured},
#endif
};

#define UART_CMD_LENGTH (sizeof(g_uart_cmd) / sizeof(g_uart_cmd[0]))

int mcmd_uart_init(mcmd_uart_t* mcmd_uart, mcmd_io_if_t *pio)
{
#if 1
    if(NULL == mcmd_uart || NULL == pio) {
        MI_LOG_ERROR( "invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }

    memset(mcmd_uart, 0, sizeof(mcmd_uart_t));
	mcmd_uart->io.create = pio->create;
	mcmd_uart->io.block_in = pio->block_in;
	mcmd_uart->io.block_quit = pio->block_quit;
	mcmd_uart->io.out = pio->out;
	mcmd_uart->io.in_byte = pio->in_byte;
	mcmd_uart->io.out_byte = pio->out_byte;
	mcmd_uart->io.destroy = pio->destroy;
	mcmd_uart->io.echo = pio->echo;
	mcmd_uart->io.handle = mcmd_uart->io.create(NULL);
	if(NULL == mcmd_uart->io.handle) {
		MI_LOG_ERROR( "io create failed");
		return MI_ERR_RESOURCES;
	}

    mcmd_uart->command_buf_size = MCMD_COMMAND_LEN_MAX;
    mcmd_uart->command_buf = malloc(mcmd_uart->command_buf_size);
    if(NULL == mcmd_uart->command_buf) {
        MI_LOG_ERROR( "command recv buf malloc failed");
        return MI_ERR_NO_MEM;
    }
    
    p_uart = mcmd_uart;
    mcmd_uart_host = mcmd_uart;
#endif
    return MI_SUCCESS;
}


void mcmd_uart_deinit(mcmd_uart_t* mcmd_uart)
{
#if 0
    if(NULL == mcmd_uart) {
        MI_LOG_ERROR( "invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return;
    }
    mcmd_uart->io.destroy(&mcmd_uart->io.handle);
	if(mcmd_uart->command_buf)
		free(mcmd_uart->command_buf);
    memset(mcmd_uart, 0, sizeof(mcmd_uart_t));
#endif
}


int mcmd_uart_send(mcmd_uart_t* mcmd_uart, int cmd, char *params, int length)
{
    unsigned short v_crc16 = 0; 
    unsigned char* p_crc16 = NULL;
    unsigned char* tmp_buff = (unsigned char *)mcmd_uart->command_buf;//[MCMD_COMMAND_LEN_MAX] = {0};
    unsigned short v_length = 0; 
    unsigned char* p_length = NULL;
    if(NULL == mcmd_uart || NULL == params || length <= 0) {
        MI_LOG_ERROR( "invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }

    memset(tmp_buff, 0, mcmd_uart->command_buf_size);
    
    tmp_buff[0] = 'L'; tmp_buff[1] = 'U';
    tmp_buff[2] = UART_CMD_VERSION; tmp_buff[3] = UART_CMD_SA;

    MI_LOG_INFO("mcmd_uart_send len %d\n", length);
    
    switch(cmd) {
        case UART_CMD_NETBIND_DEVINFO:
        case UART_CMD_NETBIND_NETINFO:
        case UART_CMD_NETBIND_DEVCERT:
        case UART_CMD_NETBIND_MANUCERT:
        case UART_CMD_NETBIND_SIGN:
        case UART_CMD_NETBIND_NETSTATUS:
        case UART_CMD_LOGIN_RSP:
        case UART_CMD_LOGIN_CFM_RSP:
        {
            //Destination  Address
            tmp_buff[4] = UART_CMD_DA_WIFI;
        }
        break;
        
        case UART_CMD_OTA_INFO:
        case UART_CMD_OTA_DATA:
        case UART_CMD_OTA_STATUS:
        case UART_CMD_OTA_REBOOT:
        {
            //Destination  Address
            tmp_buff[4] = UART_CMD_DA_MEDIAMCU;
        }
        break;
        
        case UART_CMD_SPEC_SET:
        case UART_CMD_SPEC_GET:
        case UART_CMD_SPEC_ACTION_IN:
        {
            //Destination  Address
            tmp_buff[4] = UART_CMD_DA_HOSTMCU;
        }
        break;
        
        default:
            MI_LOG_ERROR( "invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
            //Destination  Address
            tmp_buff[4] = 0;
        break;
    }
    
    //Payload Length
    v_length = 4 + length;
    p_length = (unsigned char *)&v_length;
    tmp_buff[5] = p_length[0];
    tmp_buff[6] = p_length[1];
    //Cmd ID
    tmp_buff[7] = CMD_CID(cmd);
    tmp_buff[8] = CMD_TID(cmd);
    //Cmd Length
    v_length = length;
    p_length = (unsigned char *)&v_length;
    tmp_buff[9] = p_length[0];
    tmp_buff[10] = p_length[1];
    //Cmd Value
    if(length > 0) {
        memcpy(tmp_buff + 11, params, length);
    }
    //CRC16 
    v_crc16 = crc16_ccitt(tmp_buff, 11 + length);
    p_crc16 = (unsigned char *)&v_crc16;
    tmp_buff[11 + length] = p_crc16[0];
    tmp_buff[12 + length] = p_crc16[1];
    
    tmp_buff[13 + length] = '\r';
    
    MI_LOG_INFO("mcmd_uart_send io.out len %d, buff %p\n", length+UART_CMD_MIN_LENGTH+1, tmp_buff);
    mcmd_uart->io.out(mcmd_uart->io.handle, (const char *)tmp_buff, length+UART_CMD_MIN_LENGTH+1);

    return MI_SUCCESS;
}

int mcmd_uart_handle(mcmd_uart_t* mcmd_uart, char *params, int length)
{
    int i = 0;
    if(NULL == mcmd_uart || NULL == params || length < UART_CMD_MIN_LENGTH) {
        MI_LOG_ERROR( "invalid params! %s:%d\n", (uint32_t)__FILE__, __LINE__);
        return MI_ERR_INVALID_PARAM;
    }
    if(params[0] == 'L' && params[1] == 'U' && params[4] == UART_CMD_SA) {
        for(i = 0; i < UART_CMD_LENGTH; i++) {
            if (CMD_COMBINE(params[7], params[8]) == g_uart_cmd[i].cmd_combine) {
                g_uart_cmd[i].cb_oncmd(mcmd_uart, params, length);
                break;
            }
        }
        if (i == UART_CMD_LENGTH) {
            MI_LOG_ERROR( "Not find cmd :%02x%02x\n", params[8], params[7]);
            return MI_ERR_INVALID_PARAM;
        }
    } else {
        MI_LOG_INFO( "Not my cmd :%02x%02x\n", params[8], params[7]);
        return MI_ERR_INVALID_PARAM;
    }

    return MI_SUCCESS;
}

#include "rtl876x_nvic.h"
#include "rtl876x_rcc.h"
#include "rtl876x_uart.h"
#include "rtl876x_pinmux.h"

#define TX_PIN              P3_2
#define RX_PIN              P3_3

uint32_t uart_handle = 0;
static int recv_len = 0;

static void uart_recv_handle(void *arg)
{
    mcmd_uart_handle(p_uart, p_uart->command_buf, recv_len);
    recv_len = 0;
}

static void MI_UART1(void)
{
    uint8_t rx_data;
    uint32_t int_status;
    uint32_t line_error;
    uint8_t fifo_len;

    /* read interrupt id */
    int_status = UART_GetIID(UART1);
    /* disable interrupt */
    UART_INTConfig(UART1, UART_INT_RD_AVA | UART_INT_LINE_STS, DISABLE);

    if (UART_GetFlagState(UART1, UART_FLAG_RX_IDLE) == SET)
    {
		UART_INTConfig(UART1, UART_INT_IDLE, DISABLE);
		/* TODO: Wait MCU event */
        mible_task_post(uart_recv_handle, NULL);
		UART_INTConfig(UART1, UART_INT_IDLE, ENABLE);
    }

    switch (int_status)
    {
    /* tx fifo empty */
    case UART_INT_ID_TX_EMPTY:
        /* do nothing */
        break;
    /* rx data valiable */
    case UART_INT_ID_RX_LEVEL_REACH:
    case UART_INT_ID_RX_TMEOUT:
        fifo_len = UART_GetRxFIFOLen(UART1);
        for (uint8_t i = 0; i < fifo_len; ++i)
        {
            UART_ReceiveData(UART1, &rx_data, 1);
            if(p_uart != NULL && p_uart->command_buf != NULL){
                if(recv_len < MCMD_COMMAND_LEN_MAX){
                    p_uart->command_buf[recv_len] = rx_data;
                    recv_len ++;
                }
            }else{
                printe("p_uart or buff is NULL\n");
            }
        }
        
        if(recv_len == MCMD_COMMAND_LEN_MAX){
            UART_INTConfig(UART1, UART_INT_IDLE, DISABLE);
            mible_task_post(uart_recv_handle, NULL);
            UART_INTConfig(UART1, UART_INT_IDLE, ENABLE);
        }
        break;
    /* receive line status interrupt */
    case UART_INT_ID_LINE_STATUS:
        line_error = UART1->LSR;
        printe("data_uart_isr Line status error, fail = %d!\n", line_error);
        break;
    default:
        break;
    }

    /* enable interrupt again */
    UART_INTConfig(UART1, UART_INT_RD_AVA | UART_INT_LINE_STS, ENABLE);
    return;
}

void* uart_create(void *arg)
{
    UART_DeInit(UART1);
    
    /* turn on UART clock */
    RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, ENABLE);
    
    /* pinmux config */
	Pinmux_Config(TX_PIN, UART1_TX);
	Pinmux_Config(RX_PIN, UART1_RX);
    
    /* pad config */
	Pad_Config(TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
	Pad_Config(RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
    
    /* uart init */
	UART_InitTypeDef uartInitStruct;
	UART_StructInit(&uartInitStruct);
	/* change default rx trigger level */
	uartInitStruct.rxTriggerLevel = UART_RX_FIFO_TRIGGER_LEVEL_8BYTE;
	uartInitStruct.idle_time = UART_RX_IDLE_8BYTE;
	UART_Init(UART1, &uartInitStruct);
    /* enable rx interrupt and line status interrupt */
	UART_INTConfig(UART1, UART_INT_RD_AVA | UART_INT_LINE_STS | UART_INT_IDLE, ENABLE);
    
    /*  Enable UART IRQ  */
	NVIC_InitTypeDef nvic_init_struct;
	nvic_init_struct.NVIC_IRQChannel = UART1_IRQn;
	nvic_init_struct.NVIC_IRQChannelCmd = ENABLE;
	nvic_init_struct.NVIC_IRQChannelPriority = 5;
	NVIC_Init(&nvic_init_struct);
    
    RamVectorTableUpdate(Uart1_VECTORn, MI_UART1);
    
    return &uart_handle;
}

void uart_destroy(void **handle)
{
    UART_INTConfig(UART, UART_INT_RD_AVA | UART_INT_LINE_STS | UART_INT_IDLE, DISABLE);
    UART_DeInit(UART);
    
    /* Turn off UART clock */
	RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, DISABLE);

	/* pad config */
	Pad_Config(TX_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
	Pad_Config(RX_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
    
    *handle = NULL;
}

int uart_out(void *handle, const char *buf, int vCount)
{
    uint8_t count;
    
    MI_LOG_INFO("uart_out len %d, buff %p\n", vCount, buf);
    
    while (vCount / UART_TX_FIFO_SIZE > 0)
	{
		while (UART_GetFlagState(UART1, UART_FLAG_THR_EMPTY) == 0)
			;
		for (count = UART_TX_FIFO_SIZE; count > 0; count--)
		{
			UART1->RB_THR = *buf++;
		}
		vCount -= UART_TX_FIFO_SIZE;
	}

    MI_LOG_INFO("uart_out remain %d, buff %p\n", vCount, buf);
    
	while (UART_GetFlagState(UART1, UART_FLAG_THR_EMPTY) == 0)
		;
	while (vCount--)
	{
		UART1->RB_THR = *buf++;
	}
    return 0;
}

void uart_out_byte(void *handle, unsigned char byte)
{
    while (UART_GetFlagState(UART1, UART_FLAG_THR_EMPTY) == 0)
		;
	UART1->RB_THR = byte;
}

int uart_block_in(void *handle, char *buf, int buf_size, int idle_timeout_ms)
{
    return 0;
}

void uart_block_quit(void *handle)
{

}

int uart_in_byte(void *handle, unsigned char *c, int timeout_ms)
{
    return 0;
}

#endif // #if MI_LOCK_UART_ENABLE
